/**
 * 项目名：化工企业安全生产信息化管理平台
 * 日期：    2020-02-29 21:23:48
 * Copyright (c) 2015- -版权所有
 */

package com.joysuch.wwyt.edu.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.joysuch.wwyt.common.entity.CommonType;
import com.joysuch.wwyt.common.entity.dsl.QCommonType;
import com.joysuch.wwyt.common.enums.CommonTypes;
import com.joysuch.wwyt.common.service.CommonTypeService;
import com.joysuch.wwyt.core.bean.CourseBean;
import com.joysuch.wwyt.core.bean.SimpleListBean;
import com.joysuch.wwyt.core.bean.SimpleSelectListBean;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.PageDataUtils;
import com.joysuch.wwyt.core.common.util.PageDataUtils.PageDataConvert;
import com.joysuch.wwyt.core.exception.BusinessRestrictionsException;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.service.BizCodeGeneratorFacade;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.edu.bean.*;
import com.joysuch.wwyt.edu.bean.EduCourseCoursewareListBean.SearchEduCourseCourseware;
import com.joysuch.wwyt.edu.bean.EduCourseListBean.SearchEduCourse;
import com.joysuch.wwyt.edu.entity.*;
import com.joysuch.wwyt.edu.entity.dsl.QEduCourse;
import com.joysuch.wwyt.edu.entity.dsl.QEduCourseCourseware;
import com.joysuch.wwyt.edu.entity.dsl.QEduCourseware;
import com.joysuch.wwyt.edu.entity.dsl.QEduTrainningRecordCourse;
import com.joysuch.wwyt.edu.mapper.EduCourseMapper;
import com.joysuch.wwyt.edu.mapper.EduTrainningRecordCourseMapper;
import com.joysuch.wwyt.edu.repository.*;
import com.joysuch.wwyt.edu.service.EduCourseService;
import com.joysuch.wwyt.edu.service.EduCoursewareService;
import com.joysuch.wwyt.util.PoiUtil;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.impl.JPAQueryFactory;
import javafx.util.Pair;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

@Service
@Transactional
@Slf4j
public class EduCourseServiceImpl implements EduCourseService {

    @Autowired
    private EduCourseDao dao;
    @Autowired
    private BizCodeGeneratorFacade bizCodeGeneratorFacade;
    @Autowired
    private EduCourseCoursewareDao courseCoursewareDao;
    @Autowired
    private EduCoursewareService coursewareService;
    @Autowired
    private CommonTypeService commonTypeService;
    @Autowired
    private JPAQueryFactory jpaQueryFactory;
    @Autowired
    private EduCoursewareDao eduCoursewareDao;
    @Resource
    private BaseUserDao baseUserDao;
    @Resource
    private EduExamCourseDao eduExamCourseDao;
    @Resource
    private EduExamDao eduExamDao;
    @Resource
    private EduCourseStatisticsDao eduCourseStatisticsDao;

    @Resource
    private EduCourseLearnHistoryDao eduCourseLearnHistoryDao;

    @Autowired
    private EduCourseMapper eduCourseMapper;

    @Autowired
    private EduTrainningRecordCourseMapper trainningRecordCourseMapper;

    @Autowired
    private EduTrainningRecordCourseDao trainningRecordCourseDao;
    @Override
    public EduCourse add(EduCourseEditBean entity) {
        EduCourse content = new EduCourse();
        if (StringUtils.isNotBlank(entity.getCode())) {
            Assert.isTrue(dao.countOfCode(entity.getCode()) == 0, String.format("编号%s已经存在", entity.getCode()));
            content.setCode(entity.getCode());
        } else {
            content.setCode(bizCodeGeneratorFacade.keCheng());
        }
        int number = dao.countByName(entity.getName());
        if (number > 0) {
            throw new IllegalArgumentException("课程类型名称已存在");
        }
        BeanUtils.copyProperties(entity, content, "id", "code");
        content.setCoursewareCount(0);
        return dao.save(content);
    }

    @Override
    public void update(EduCourseEditBean entity) {
        EduCourse content = dao.findById(entity.getId()).get();
        // Assert.notNull(entity.getCode(), "编号不能为空");
        // Assert.isTrue(dao.countOfCodeExcludeId(entity.getCode(), entity.getId()) == 0,
        // String.format("编号%s已经存在", entity.getCode()));
        // content.setCode(entity.getCode());
        BeanUtils.copyProperties(entity, content, "id", "code");
        EduCourse save = dao.save(content);
        int number = dao.countByName(entity.getName());
        if (number > 1) {
            throw new IllegalArgumentException("课程类型名称已存在");
        }
    }

    @Override
    public Page<EduCourseListBean> findByPage(Pageable page, SearchEduCourse condition) {
        Predicate predicate = prepareListSearchCondition(condition);

        Page<EduCourse> data = predicate == null ? dao.findAll(page) : dao.findAll(predicate, page);

        return PageDataUtils.convertPageData(data, new PageDataConvert<EduCourseListBean, EduCourse>() {

            @Override
            public EduCourseListBean convert(EduCourse f) {
                EduCourseListBean bean = new EduCourseListBean();
                BeanUtils.copyProperties(f, bean);
                return bean;
            }

        });
    }

    private Predicate prepareListSearchCondition(SearchEduCourse condition) {
        Predicate p = null;
        QEduCourse q = QEduCourse.eduCourse;
        if (condition.getNameOrCode() != null) {
            p = ExpressionUtils.or(q.code.contains(condition.getNameOrCode()),
                    q.name.contains(condition.getNameOrCode()));
        }
        return p;
    }

    @Override
    public void delete(Long id) throws BusinessRestrictionsException {
        // 校验业务逻辑是否允许删除。如本数据被其他业务关联，删除后导致其他业务不可用。不能删除时将抛出业务异常
        checkBussinessRestrictions(id);
        dao.deleteById(id);
        // 删除绑定的课程
        courseCoursewareDao.deleteByCourseId(id);
    }

    /**
     * 校验业务逻辑是否允许删除。如本数据被其他业务关联，删除后导致其他业务不可用
     *
     * @param id 要删除的业务ID
     */
    private void checkBussinessRestrictions(Long id) throws BusinessRestrictionsException {
        long count = courseCoursewareDao.count(QEduCourseCourseware.eduCourseCourseware.courseId.eq(id));
        Assert.isTrue(count == 0, "课程已绑定课件，必须先删除绑定的课件，才可以删除课程");
        long countCourse = trainningRecordCourseMapper.selectCourse(id);
        Assert.isTrue(countCourse == 0, "课程已被培训关联，无法新增或删除课件");
    }

    @Override
    public EduCourse findById(Long id) {
        return dao.findById(id).orElse(null);
    }

    @Override
    public EduCourseDetailBean getById(Long id) {
        EduCourse entity = findById(id);
        if (entity == null) {
            return null;
        }
        EduCourseDetailBean bean = new EduCourseDetailBean();
        BeanUtils.copyProperties(entity, bean);
        return bean;
    }

    @Override
    public Page<EduCoursewareListBean> findCoursewareByPage(Pageable page, SearchEduCourseCourseware condition) {
        return coursewareService.findByPage(page, createCondition(condition));
    }

    private Predicate createCondition(SearchEduCourseCourseware condition) {
        QEduCourseware q = QEduCourseware.eduCourseware;
        Predicate p = q.id.in(courseCoursewareDao.findCoursewareIdsByCourseId(condition.getCourseId()));
        if (condition.getFormat() != null) {
            p = ExpressionUtils.and(p, q.format.eq(condition.getFormat()));
        }
        if (condition.getTypeId() != null) {
            p = ExpressionUtils.and(p, q.typeId.eq(condition.getTypeId()));
        }
        if (condition.getNameOrCodeOrCreateUserName() != null) {
            String s = condition.getNameOrCodeOrCreateUserName();
            Predicate or = ExpressionUtils.or(q.name.contains(s),
                    ExpressionUtils.or(q.code.contains(s), q.createUserName.contains(s)));
            p = ExpressionUtils.and(p, or);
        }
        return p;
    }

    @Override
    public void batchAddCourseware(Long courseId, List<Long> coursewareIds) {
        List<Long> exists = courseCoursewareDao.findCoursewareIdsByCourseId(courseId);
        //存进来的课件没有存过 保存
        for (Long id : coursewareIds) {
            if (!CollectionUtils.isEmpty(exists)) {
                if (!exists.contains(id)) {
                    EduCourseCourseware cc = new EduCourseCourseware();
                    cc.setCourseId(courseId);
                    cc.setCoursewareId(id);
                    courseCoursewareDao.save(cc);
                }
            } else {
                EduCourseCourseware cc = new EduCourseCourseware();
                cc.setCourseId(courseId);
                cc.setCoursewareId(id);
                courseCoursewareDao.save(cc);
            }
        }
        //存进来的课程比数据库的少 数据多余数据库绑定关系删除
        for (Long ex : exists) {
            if (!CollectionUtils.isEmpty(coursewareIds)) {
                if (!coursewareIds.contains(ex)) {
                    EduCourseCourseware oec = courseCoursewareDao.findByCoursewareIdAndCourseId(ex, courseId);
                    if (oec != null) {
                        courseCoursewareDao.delete(oec);
                    }
                }
            }
        }
        long countCourse = trainningRecordCourseMapper.selectCourse(courseId);
        Assert.isTrue(countCourse == 0, "课程已被培训关联，无法新增或删除课件");
        dao.updateCoursewarecount(courseId);
    }

    @Override
    public void deleteCourseware(Long courseId, Long coursewareId) {
        long countCourse = trainningRecordCourseMapper.selectCourse(courseId);
        Assert.isTrue(countCourse == 0, "课程已被培训关联，无法新增或删除课件");
        courseCoursewareDao.deleteByCourseIdAndCoursewareId(courseId, coursewareId);
        dao.updateCoursewarecount(courseId);
    }

    @Override
    public Page<EduCoursewareDetailBean> queryCoursewareList(Long courseId, Long coursewareTypeId, Pageable pageable) {
        return coursewareService.findCoursecenterPage(courseId, coursewareTypeId, pageable);
    }

    @Override
    public List<SimpleSelectListBean> queryCoursewareTypeListOfCourse(Long courseId) {
        if (courseId == null) {
            return commonTypeService.simpleList(CommonTypes.COURSEWARE);
        } else {
            QEduCourseCourseware course = QEduCourseCourseware.eduCourseCourseware;
            QEduCourseware courseware = QEduCourseware.eduCourseware;
            QCommonType type = QCommonType.commonType;
            List<CommonType> fetch = jpaQueryFactory.select(type).from(course, courseware, type)
                    .where(course.courseId.eq(courseId), course.coursewareId.eq(courseware.id),
                            courseware.typeId.eq(type.id))
                    .distinct().fetch();
            return PageDataUtils.convertListData(fetch, new PageDataConvert<SimpleSelectListBean, CommonType>() {

                @Override
                public SimpleSelectListBean convert(CommonType f) {
                    SimpleSelectListBean bean = new SimpleSelectListBean();
                    bean.setValue(f.getId());
                    bean.setLabel(f.getName());
                    return bean;
                }
            });
        }
    }

    @Override
    public ResultBean eduCourse() {
        List<EduCourse> list = dao.findAll();
        List<EduCourseCountDto> dtoList = Lists.newArrayList();
        for (EduCourse course : list) {
            dtoList.add(new EduCourseCountDto(course.getName(), course.getCoursewareCount()));
        }
        return ResultBean.success(dtoList);
    }

    @Override
    public List<SimpleListBean> findCoursewareByList(Long courseId) {
        List<SimpleListBean> list = new ArrayList<>();
        List<Long> ids = courseCoursewareDao.findCoursewareIdsByCourseId(courseId);
        for (Long id : ids) {
            Optional<EduCourseware> byId = eduCoursewareDao.findById(id);
            if (byId.isPresent()) {
                SimpleListBean bean = new SimpleListBean();
                bean.setName(byId.get().getName());
                bean.setId(byId.get().getId());
                list.add(bean);
            }
        }
        return list;
    }

    @Override
    public List<CourseBean> findAll(String name) {
     //  List<CourseBean> list=eduCourseMapper.findAll(name, Context.getCurrentUserId());
        List<CourseBean> list=new ArrayList<>();
        List<EduCourse> all = dao.findByName(name);
        if (!CollectionUtils.isEmpty(all)){
            for (EduCourse eduCourse : all) {
                CourseBean courseBean=new CourseBean();
                courseBean.setLabel(eduCourse.getName());
                courseBean.setValue(eduCourse.getId());
                courseBean.setOpenFace(eduCourse.getOpenFace());
                list.add(courseBean);
            }
        }
        return list;
    }

    @Override
    public Page<EduCourseVo> coursePage(Pageable page, SearchEduCourse condition) {
        Predicate predicate = prepareListSearchCondition(condition);

        Page<EduCourse> data = predicate == null ? dao.findAll(page) : dao.findAll(predicate, page);
        //统计学员人数，目前是所有人
        Integer usersNum = baseUserDao.getEffectiveUserCount();
        return PageDataUtils.convertPageData(data, f -> {
            EduCourseVo bean = new EduCourseVo();
            bean.setCourseId(f.getId());
            bean.setCourseName(f.getName());
            //统计学时
            List<Long> ids = courseCoursewareDao.findCoursewareIdsByCourseId(f.getId());
            Integer minutes = 0;
            for (Long id : ids) {
                Optional<EduCourseware> byId = eduCoursewareDao.findById(id);
                if (byId.isPresent()) {
                    EduCourseware eduCourseware = byId.get();
                    if (eduCourseware.getMinute() != null){
                        minutes += eduCourseware.getMinute();
                    }
         /*           else {
                        int maxMinute = eduCoursewareDao.selectBydocTypeMaxMinute(eduCourseware.getDocType());
                        minutes += maxMinute;
                    }*/
                }
            }
            bean.setCreditHours(getCreditHours(minutes.longValue()));
            bean.setLearnerNum(usersNum);
            //查询关联考试
            List<String> names = Lists.newArrayList();
            List<EduExamCourse> examCourses = eduExamCourseDao.findByCourseId(f.getId());
            examCourses.forEach(examCourse -> {
                names.add(eduExamDao.getNameById(examCourse.getExamId()));
            });
            bean.setExams(String.join(",",names));
            if (CollectionUtils.isEmpty(ids)){
                bean.setFinishLearnNum(0);
                return bean;
            }
            //查询完成学习人数
            List<Long> userIds = eduCourseStatisticsDao.countLearnNum(ids);
            List<Long> videoIds = eduCoursewareDao.findVideo(ids);
            if (CollectionUtils.isEmpty(videoIds)){
                bean.setFinishLearnNum(CollectionUtils.isEmpty(userIds)? 0 : userIds.size());
                return bean;
            }
            AtomicReference<Integer> learnNum = new AtomicReference<>(0);
            if (!CollectionUtils.isEmpty(userIds)){
                userIds.forEach(userId ->{
                    AtomicBoolean flag = new AtomicBoolean(true);
                    videoIds.forEach(coursewareId->{
                        Integer count = eduCourseLearnHistoryDao.countState(coursewareId,userId);
                        if (count <= 0){
                            flag.set(false);
                        }
                    });
                    if (flag.get()){
                        learnNum.updateAndGet(v -> v + 1);
                    }
                });
            }
            bean.setFinishLearnNum(learnNum.get());
            return bean;
        });
    }

    @Override
    public void courseExport(SearchEduCourse condition, HttpServletRequest request, HttpServletResponse response) {
        // 所有课程
        List<EduCourseVo> dataList = courseList(condition);
        String file = "课程统计" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN) + ".xls".trim();
        response.setContentType(request.getServletContext().getMimeType(file));
        HSSFWorkbook hwb = null;
        try {
            response.setHeader("Content-Disposition","attachment;filename=" + new String(file.getBytes("GBK"), "iso8859-1"));
            List<String> cellNames = Arrays.asList("课程名称", "学时", "学员人数", "学习完成人数", "关联考试");
            Pair<HSSFWorkbook, HSSFSheet> pair = PoiUtil.getPoi("课程统计", cellNames);
            addCourseRecordData(dataList, pair);
            hwb = pair.getKey();
            hwb.write(response.getOutputStream());
        } catch (IOException e) {
            log.error("课程统计excel导出失败：" + e.getMessage());
            throw new IllegalArgumentException("excel导出失败，请联系系统管理员");
        } finally {
            if (hwb != null) {
                try {
                    hwb.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public List<EduCourseVo> courseList(SearchEduCourse condition) {
        Predicate predicate = prepareListSearchCondition(condition);

        List<EduCourse> data = predicate == null ? dao.findAll() : (List<EduCourse>)dao.findAll(predicate);
        if (!CollectionUtils.isEmpty(data)){
            data.sort(Comparator.comparing(EduCourse::getId).reversed());
        }
        //统计学员人数，目前是所有人
        Integer usersNum = baseUserDao.getEffectiveUserCount();
        List<EduCourseVo> list = Lists.newArrayList();
        data.forEach(course -> {
            EduCourseVo bean = new EduCourseVo();
            bean.setCourseId(course.getId());
            bean.setCourseName(course.getName());
            //统计学时
            List<Long> ids = courseCoursewareDao.findCoursewareIdsByCourseId(course.getId());
            Integer minutes = 0;
            for (Long id : ids) {
                Optional<EduCourseware> byId = eduCoursewareDao.findById(id);
                if (byId.isPresent()) {
                    if (byId.get().getMinute() != null){
                        minutes += byId.get().getMinute();
                    }
                }
            }
            bean.setCreditHours(getCreditHours(minutes.longValue()));
            bean.setLearnerNum(usersNum);
            //查询关联考试
            List<String> names = Lists.newArrayList();
            List<EduExamCourse> examCourses = eduExamCourseDao.findByCourseId(course.getId());
            examCourses.forEach(examCourse -> {
                names.add(eduExamDao.getNameById(examCourse.getExamId()));
            });
            bean.setExams(String.join(",",names));
            //查询完成学习人数
            List<Long> userIds = eduCourseStatisticsDao.countLearnNum(ids);
            bean.setFinishLearnNum(CollectionUtils.isEmpty(userIds)? 0 : userIds.size());
            List<Long> videoIds = Lists.newArrayList();
            if (!CollectionUtils.isEmpty(ids)){
                videoIds = eduCoursewareDao.findVideo(ids);
            }else {
                bean.setFinishLearnNum(0);
            }
            if (!CollectionUtils.isEmpty(userIds) && !CollectionUtils.isEmpty(videoIds)){
                AtomicReference<Integer> learnNum = new AtomicReference<>(0);
                List<Long> finalVideoIds = videoIds;
                userIds.forEach(userId ->{
                    AtomicBoolean flag = new AtomicBoolean(true);
                    finalVideoIds.forEach(coursewareId->{
                        Integer count = eduCourseLearnHistoryDao.countState(coursewareId,userId);
                        if (count <= 0){
                            flag.set(false);
                        }
                    });
                    if (flag.get()){
                        learnNum.updateAndGet(v -> v + 1);
                    }
                });
                bean.setFinishLearnNum(learnNum.get());
            }
            list.add(bean);
        });
        return list;
    }

    public void addCourseRecordData(List<EduCourseVo> dataList, Pair<HSSFWorkbook, HSSFSheet> pair) {
        HSSFSheet sheet = pair.getValue();
        for (int i = 0; i < dataList.size(); i++) {
            EduCourseVo vo = dataList.get(i);
            HSSFRow row = sheet.createRow(i + 1);
            row = sheet.createRow(i + 1);
            row.createCell(0).setCellValue(vo.getCourseName());
            row.createCell(1).setCellValue(vo.getCreditHours());
            row.createCell(2).setCellValue(vo.getLearnerNum());
            row.createCell(3).setCellValue(vo.getFinishLearnNum());
            row.createCell(4).setCellValue(vo.getExams());
        }
    }

    private String getCreditHours(Long minutes){
        String FORMAT ="%02d小时%02d分钟%02d秒";
        return String.format(FORMAT,
                TimeUnit.MINUTES.toHours(minutes),
                TimeUnit.MINUTES.toMinutes(minutes) - TimeUnit.HOURS.toMinutes(
                        TimeUnit.MINUTES.toHours(minutes)),
                TimeUnit.MINUTES.toSeconds(minutes) - TimeUnit.MINUTES.toSeconds(
                        TimeUnit.MINUTES.toMinutes(minutes)));
    }

}
